package no.sachmuld.methodcombination.api;

import no.sachmuld.methodcombination.api.reflection.component.MethodCombination;
import no.sachmuld.methodcombination.impl.validate.BehaviourMethodValidatorImpl;
import no.sachmuld.methodcombination.impl.validate.MethodCombinationValidationError;
import no.sachmuld.methodcombination.impl.validate.TargetMethodIsStatic;
import no.sachmuld.methodcombination.impl.validate.TargetMethodNotFinal;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 *
 */
public abstract class MethodCombinationValidator {

    public List<MethodCombinationValidationError> validate() {

        List<MethodCombinationValidationError> errors = new ArrayList<>();

        Class targetClass = findTargetClass(errors);
        Class behaviourClass = findBehaviourClass(errors);

        if (targetClass != null && behaviourClass != null) {

            BehaviourMethodValidatorImpl validator = new BehaviourMethodValidatorImpl(behaviourClass, targetClass, errors);

            Set<Method> targetMethods = findTargetMethods(errors);

            for (Method method : targetMethods) {

                if (!Modifier.isFinal(method.getModifiers())) {
                    errors.add(new TargetMethodNotFinal(targetClass, method));
                }

                if (Modifier.isStatic(method.getModifiers())) {
                    errors.add(new TargetMethodIsStatic(targetClass, method));
                }

                MethodCombination methodCombination = findMethodCombination(method, errors);

                methodCombination.validate(validator);

            }

            validator.atEnd();

        }

        return errors;

    }

    protected abstract Class findTargetClass(List<MethodCombinationValidationError> errors);
    protected abstract Class findBehaviourClass(List<MethodCombinationValidationError> errors);
    protected abstract Set<Method> findTargetMethods(List<MethodCombinationValidationError> errors);
    protected abstract MethodCombination findMethodCombination(Method method, List<MethodCombinationValidationError> errors);
}
